<template>
  <section style="width: 100%;height: 50px;margin-bottom: 5px">
    <el-button type="primary" :icon="Operation" @click="handleOpenMultitask">
      {{ multitasking ? '关闭多工作簿任务' : '开启多工作簿任务' }}
    </el-button>
    <el-button :icon="CopyDocument" @click="handleSave" type="success">保存工作簿到磁盘</el-button>
    <el-button :icon="Download" @click="handleExport" type="success">下载工作簿</el-button>
    <el-button :icon="Link" type="warning" @click="handleGetMultipleData">一次性从服务端获取公式结果</el-button>
    <el-button style="margin: 0 10px" type="warning" @click="clearWorkBook(false)">清空工作簿</el-button>
    <el-button type="warning" @click="clearWorkBook(true)" style="margin: 10px 0">重置工作簿区域</el-button>
    <el-button type="warning" @click="reCalculateFormulaValue" style="margin: 10px 0">刷新公式值</el-button>
    <!--    <el-input :model-value="process"></el-input>-->
  </section>
  <section class="index-container el-card" v-loading="pageLoading">
    <div class="index-container-workbook" :style="{ width:multitasking ? '65%':'80%' }">
      <gc-spread-sheets id="ss" class="sample-spreadsheets" @workbookInitialized="initSpread">
        <gc-worksheet>
        </gc-worksheet>
        <gc-worksheet>
        </gc-worksheet>
        <gc-worksheet>
        </gc-worksheet>
      </gc-spread-sheets>
      <div id="statusBar"></div>
    </div>
    <div class="index-container-multitask el-card" :style="{ width:multitasking ? '15%':'0%' }">
      <el-card v-for="(item,index) in multiWorkBookList" :key="index"
               @click="handleSpreadDialog(index)"
               style="margin-bottom: 15px">
        <template #header>
          <span>工作簿索引：{{ index }}</span>
        </template>
        <img style="width: 100%" src="../../src/assets/img.png" alt=""/>
        <span>{{ item.name }}</span>
        <gc-spread-sheets style="width: 0;height: 0;opacity: 1;overflow: hidden;display: none"
                          @workbookInitialized="spread=>initMultiSpread(spread,index)"></gc-spread-sheets>
      </el-card>
    </div>
    <div class="index-container-operations">
      <div class="el-card" style="padding: 5px;margin-bottom: 20px">
        <el-button :icon="Memo">磁盘文件列表</el-button>
        <div v-for="item in savedFileList" @click="handleSavedFile(item)"
             @contextmenu.stop.prevent="handleToMultiWork(item)"
             :style="{ backgroundColor:spreadFileName==item ? '#d9e8f6' : 'transparent' }">
          <div class="file-list-item">
            <span>{{ item }}</span>
            <el-icon @click.stop="handleDeleteFile(item)">
              <Close/>
            </el-icon>
          </div>
        </div>
      </div>
      <div class="el-card" style="padding: 5px;margin-bottom: 20px">
        <el-upload :show-file-list="false" v-model:file-list="fileList" :auto-upload="false" :accept="['.xlsx','.xlx']"
                   :on-change="handleFileChange">
          <el-button :icon="Files">缓存文件（点击上传）</el-button>
        </el-upload>
        <div class="file-list">
          <div class="file-list-item" v-for="(file,index) in fileList" :key="file.uid"
               @click="handleCacheFile(index)"
               :style="{ backgroundColor:spreadFileName==file.name ? '#d9e8f6' : 'transparent' }">
            <span>{{ file.name }}</span>
            <el-icon @click.stop="handleDeleteCacheFile(index)">
              <Close/>
            </el-icon>
          </div>
        </div>
      </div>
      <div class="el-card" style="padding: 5px">
        公式列表
        <ul class="formula-list">
          <li class="formula-list-item" v-for="(item,index) in formulaList" :key="index">
            <span>{{ item }}</span>
          </li>
        </ul>
        多WorkBook公式
        <ul class="formula-list">
          <li>GETWORKBOOK</li>
        </ul>
      </div>
    </div>
    <el-dialog v-model="dialogVisible" title="工作簿查看" :width="1000">
      <section style="width: 100%;height: 500px">
        <gc-spread-sheets style="width: 100%;height: 100%" @workbookInitialized="initDialogSpread"></gc-spread-sheets>
      </section>
    </el-dialog>
    <!--  如果没有提供函数来解决，就用组件的方式  -->
    <gc-spread-sheets style="position: fixed;left: -500px;top:0;width: 100px;height: 100px"
                      @workbookInitialized="initOtherSpread">
      <gc-worksheet>
      </gc-worksheet>
    </gc-spread-sheets>
  </section>
</template>

<script setup lang="ts">
import GC from "@grapecity-software/spread-sheets";
import {onMounted, ref, toRaw} from "vue";
import '@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013lightGray.css'
import '@grapecity-software/spread-sheets-print';
import "@grapecity-software/spread-sheets-vue";
import "@grapecity-software/spread-sheets-io";
import '@grapecity-software/spread-sheets-shapes';
import '@grapecity-software/spread-sheets-charts';
import '@grapecity-software/spread-sheets-slicers';
import '@grapecity-software/spread-sheets-pivot-addon';
import '@grapecity-software/spread-sheets-reportsheet-addon';
import "@grapecity-software/spread-sheets-tablesheet";
import "@grapecity-software/spread-sheets-ganttsheet";
import '@grapecity-software/spread-sheets-resources-zh';
import type {UploadFile, UploadUserFile} from 'element-plus'
import {Operation, CopyDocument, Download, Link, Memo, Files} from '@element-plus/icons-vue'
import type {FileType} from './constant.ts'
import {
  getFileType,
  mapExportFileType,
  openOptions,
  openOptionsConfig,
  saveOptions,
  saveOptionsConfig
} from './constant.ts'
import {Close} from '@element-plus/icons-vue'


GC.Spread.Common.CultureManager.culture("zh-cn");

interface CellRequest {
  url: string;
  body: string;
  context: any;
}

/**
 * 数字类型传递，文本要双引号
 * 建一个工厂类，根据后端传来的ASUM，BSUM生成类，继承自GC.Spread.CalcEngine.Functions.AsyncFunction，传参跟现有的ASUM一样
 */
class CustomFunctionFactory extends GC.Spread.CalcEngine.Functions.AsyncFunction {
  constructor(name: string) {
    super(name, 1, 255);
  }

  defaultValue(): any {
    return "Loading";
  }

  evaluateAsync(context: GC.Spread.CalcEngine.AsyncEvaluateContext): any {
    const args = arguments;
    console.log(args)
    // 把args下载到本地
    const p = arguments[0]
    // 实际参数从1 开始
    let parmList = []
    for (let i = 1; i < args.length; i++) {
      parmList.push(args[i])
    }
    const params = new URLSearchParams({
      formula: `${this.name}`,
    });
    // 将请求参数收集到全局数组中
    cellRequestMap.set(p.row + '-' + p.col, {
      url: `http://localhost:3000/formula?${params}`,
      body: JSON.stringify(parmList),
      context: context
    });

    // 设置默认值,默认值是文本类型,和其他的一起的时候会报#NAME?
    context.setAsyncResult("Loading...");
  }
}

class GetWorkBookFunction extends GC.Spread.CalcEngine.Functions.Function {
  constructor() {
    super("GETWORKBOOK", 1, 10);
  }

  evaluate(...args: any) {
    if (!multitasking.value) return "当前场景无效"
    if (multiWorkBookList.value.length === 0) return "无工作簿调度中"
    if (args.length !== 3) return "error"
    const otherSpread = toRaw(otherSpreadRef.value)
    let sheet = otherSpread?.getSheet(0)
    sheet?.setFormula(0, 0, `=COLUMN(${args[2]})`)
    sheet?.setFormula(0, 1, `=ROW(${args[2]})`)
    const col = sheet?.getValue(0, 0)
    const row = sheet?.getValue(0, 1)
    const tempSpread = toRaw(multiWorkBookList.value[args[0]].spread)
    sheet = tempSpread?.getSheet(args[1])
    return sheet?.getValue(row - 1, col - 1) || "error"
  }
}

GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("GETWORKBOOK", new GetWorkBookFunction())


let pageLoading = ref(false)
let multitasking = ref(false); // 是否多任务中
let multiWorkBookList = ref<any[]>([])
let dialogVisible = ref(false)
let dialogWorkBookIndex = ref(-1)

// 开启多任务
function handleOpenMultitask() {
  pageLoading.value = true
  multitasking.value = !multitasking.value;
  multiWorkBookList.value = []
  setTimeout(() => {
    const spread = toRaw(spreadRef.value)
    spread?.refresh()
    pageLoading.value = false
  }, 1000)
}

const spreadRef = ref<GC.Spread.Sheets.Workbook>();
let registerFormula = ref(false)
let formulaList = ref<string[]>([])
const initSpread = async function (spread: GC.Spread.Sheets.Workbook) {
  if (!registerFormula.value) {
    console.log('注册全局公式中')
    const res = await fetch('http://localhost:3000/formula/list')
    if (res.ok) {
      const data = await res.json()
      formulaList.value = data
      data.forEach((item: any) => {
        GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction(item, new CustomFunctionFactory(item))
      })
      registerFormula.value = true
    }
  }

  spreadRef.value = spread;

  const sheet1 = spread.getSheet(0);
  sheet1.suspendPaint();
  sheet1.setArray(0, 0, [[5, 15, 24, 1, 3, 5, 23, 4]]);
  let i = 14;
  while (i--) {
    sheet1.setColumnWidth(i, 120)
    sheet1.setRowHeight(i, 20)
  }
  sheet1.name("表格一")
  // sheet1.addCustomFunction(new FactorialFunction())

  sheet1.setValue(2, 2, "ASUM(acc,B1)")
  // sheet1.setFormula(2, 3, 'ASUM(B1)')
  // sheet1.setFormula(3, 3, 'ASUM("acc",B1)')
  sheet1.setFormula(4, 3, 'BCUM("acc",B1,C1)')
  // sheet1.setFormula(5, 3, 'CSUM("acc",B1,C1,D1)')
  // sheet1.setFormula(7, 3, 'CSUM("acc",B1,C1,D1)')
  // sheet1.setFormula(8, 3, 'CSUM("acc",B1,C1,D1)')
  // sheet1.bind(GC.Spread.CalcEngine.)

  // sheet1.setFormula(13, 4, 'GETWORKBOOK(0,0,"A1")')
  // sheet1.setFormula(14, 5, 'COLUMN(A1)')

  sheet1.setValue(6, 2, "workBook1")
  // sheet1.setFormula(6, 3, `'[模板一.xlsx]Sheet1'!A2`)

  spread.getExternalReferences().forEach(item => {
    let data = {Sheet1: [["Score:"], [parseInt(String(Math.random() * 50 + 50))], [parseInt(String(Math.random() * 50 + 50))]]};
    console.log(item)
    spread.updateExternalReference('模板一.xlsx', data, item.filePath)
  })
  sheet1.bind(GC.Spread.Sheets.Events.CellClick, function (e, args) {
    console.log(e, args)
  })
  sheet1.resumePaint();

  const sheet2 = spread.getSheet(1);
  sheet2.suspendPaint();
  sheet2.name("表格2")
  sheet2.setFormula(0, 0, "=Sheet1!A1")
  // sheet2.setFormula(3, 3, 'ASUM("acc",B1)')
  sheet2.setFormula(4, 3, 'BCUM("acc",B1,C1)')
  sheet2.resumePaint();

  const sheet3 = spread.getSheet(2);
  sheet3.suspendPaint();
  sheet3.setFormula(0, 0, "=Sheet1!B1-Sheet2!A1")


  sheet3.resumePaint();
  // 底部状态栏：就绪、缩放，支持自定义
  const statusBarElement = document.getElementById('statusBar');
  if (statusBarElement) {
    let statusBar = new GC.Spread.Sheets.StatusBar.StatusBar(statusBarElement);
    statusBar.bind(spread);
  }
}

const dialogSpreadRef = ref<GC.Spread.Sheets.Workbook>();

function handleSpreadDialog(index: number) {
  dialogWorkBookIndex.value = index
  dialogVisible.value = true
  if (dialogSpreadRef.value) {
    const spread = toRaw(dialogSpreadRef.value)
    const blob = multiWorkBookList.value[dialogWorkBookIndex.value].blob
    spread.clearSheets()
    const file = new File([blob], multiWorkBookList.value[dialogWorkBookIndex.value].name)
    let fileType = getFileType(file);
    let options = getOptions('open');
    if (fileType === 'sjs') {
      spread.open(file,
          function () {
          },
          function () {
          },
          options);
    } else {
      spread.import(file, function () {
        spread.removeSheet(0) // 删除默认的 授权说明sheet
      }, function () {
      }, options);
    }
  }
}

function initDialogSpread(spread: GC.Spread.Sheets.Workbook) {
  const blob = multiWorkBookList.value[dialogWorkBookIndex.value].blob
  dialogSpreadRef.value = spread;
  spread.clearSheets()
  const file = new File([blob], multiWorkBookList.value[dialogWorkBookIndex.value].name)
  let fileType = getFileType(file);
  let options = getOptions('open');
  if (fileType === 'sjs') {
    spread.open(file,
        function () {
        },
        function () {
        },
        options);
  } else {
    spread.import(file, function () {
      spread.removeSheet(0) // 删除默认的 授权说明sheet
    }, function () {
    }, options);
  }
}

const otherSpreadRef = ref<GC.Spread.Sheets.Workbook>();

function initOtherSpread(spread: GC.Spread.Sheets.Workbook) {
  otherSpreadRef.value = spread;
}

function initMultiSpread(spread: GC.Spread.Sheets.Workbook, index: number) {
  multiWorkBookList.value[index].spread = spread;
  const blob = multiWorkBookList.value[index].blob
  spread.clearSheets()
  const file = new File([blob], multiWorkBookList.value[index].name)
  let fileType = getFileType(file);
  let options = getOptions('open');
  if (fileType === 'sjs') {
    spread.open(file,
        function () {
        },
        function () {
        },
        options);
  } else {
    spread.import(file, function () {
      spread.removeSheet(0) // 删除默认的 授权说明sheet
    }, function () {
    }, options);
  }
}

let cellRequestMap = new Map<string, CellRequest>(); // 数组索引是不一样的,所以会重建
let fileList = ref<UploadUserFile[]>([]);
const openFileType = ref<FileType>('xlsx');
const saveFileType = ref<FileType>('xlsx');
const getOptions = function (mode: string) {
  let optionsConfig, optionsValue;
  if (mode === 'open') {
    optionsConfig = openOptionsConfig[openFileType.value];
    optionsValue = openOptions;
  } else {
    optionsConfig = saveOptionsConfig[saveFileType.value];
    optionsValue = saveOptions;
  }

  let options: any = {};
  optionsConfig.forEach((prop: any) => {
    // @ts-ignore
    let v = optionsValue[prop.propName];
    if (prop.type === 'number') {
      v = +v;
    }
    options[prop.propName] = v;
  });

  return options;
}
// 当前spread查看的文件名
let spreadFileName = ref('');
let process = ref(0)

// 一次性从服务端获取异步函数结果
async function handleGetMultipleData() {
  console.log(cellRequestMap);
  process.value = 0
  let reqList = Array.from(cellRequestMap.values());
  let processItem = Math.floor(100 / reqList.length)
  for (const request of reqList) {
    try {
      const response = await fetch(request.url, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: request.body
      });

      const data = await response.json();
      request.context.setAsyncResult(data.result);
      process.value += processItem
      console.log(`Request completed for ${request.url}:`, data.result);
    } catch (error) {
      process.value += processItem
      request.context.setAsyncResult("Error");
      console.error(`Request failed for ${request.url}:`, error);
    }
  }
  process.value = 100
  cellRequestMap.clear();
  console.log("All requests completed");
}


// 删除磁盘文件
function handleDeleteFile(e: string) {
  fetch('http://localhost:3000/template/' + e, {
    method: 'DELETE',
  }).then(res => {
    if (res.ok) {
      apiGetFileList()
    }
  })
}

// 保存文件到磁盘
function handleSave() {
  let fileType = saveFileType.value;
  let fileName = spreadFileName.value || (Date.now() + '.xlsx');
  let options = getOptions('save');
  let spread = toRaw(spreadRef.value)
  if (spread == undefined) return;
  if (fileType === 'sjs') {
    spread.save(function (blob: Blob) {
      // 保存文件
      let file = new File([blob], fileName)
      let formData = new FormData();
      formData.append('file', file);
      // 把json往本地文件log.json里续写进去
      fetch('http://localhost:3000/log', {
        method: 'POST',
        body: formData
      }).then(res => {
        if (res.ok) {
          apiGetFileList()
        }
      }).catch(err => {
        console.error('Error:', err);
      });
    }, function () {
    }, options);
  } else {
    options.fileType = mapExportFileType(fileType);
    spread.export(function (blob: Blob) {
      let file = new File([blob], encodeURIComponent(fileName))
      let formData = new FormData();
      formData.append('file', file);
      // 把json往本地文件log.json里续写进去
      fetch('http://localhost:3000/log', {
        method: 'POST',
        body: formData
      }).then(res => {
        if (res.ok) {
          let i = fileList.value.findIndex((item) => item.name === spreadFileName.value)
          if (i !== -1) fileList.value.splice(i, 1)
          apiGetFileList()
        } else {
          console.error('Failed to write log');
        }
      }).catch(err => {
        console.error('Error:', err);
      });
    }, function () {
    }, options);
  }
}

function handleExport() {
  let fileType = saveFileType.value;
  let fileName = spreadFileName.value || (Date.now() + '.xlsx');
  let options = getOptions('save');
  let spread = toRaw(spreadRef.value)
  if (spread == undefined) return;
  if (fileType === 'sjs') {
    spread.save(function (blob: Blob) {
      // 保存文件
      let a = document.createElement('a');
      a.href = URL.createObjectURL(blob);
      a.download = fileName;
      a.click();
      URL.revokeObjectURL(a.href);
    }, function () {
    }, options);
  } else {
    options.fileType = mapExportFileType(fileType);
    spread.export(function (blob: Blob) {
      let a = document.createElement('a');
      a.href = URL.createObjectURL(blob);
      a.download = fileName;
      a.click();
      URL.revokeObjectURL(a.href);
    }, function () {
    }, options);
  }
}

// 查看磁盘文件
function handleSavedFile(e: string) {
  spreadFileName.value = e;
  fetch('http://localhost:3000/template/' + e, {
    method: 'GET',
  }).then(res => {
    if (res.ok) {
      res.blob().then(blob => {
        const file = new File([blob], e)
        let fileType = getFileType(file);
        let options = getOptions('open');
        let spread = toRaw(spreadRef.value)
        if (spread == undefined) return;
        console.log(options, fileType)
        if (fileType === 'sjs') {
          spread.open(file,
              function () {
              },
              function () {
              },
              options);
        } else {
          spread.import(file, function () {
            spread.removeSheet(0) // 删除默认的 授权说明sheet
          }, function () {
          }, options);
        }
      })
    }
  })
}

// 把文件加入缓存
function handleToMultiWork(e: string) {
  fetch('http://localhost:3000/template/' + e, {
    method: 'GET',
  }).then(res => {
    if (res.ok) {
      res.blob().then(blob => {
        multiWorkBookList.value.push({
          name: e,
          blob: blob,
          spread: null
        })
      })
    }
  })
}

// upload组件选择文件
function handleFileChange(uploadFile: UploadFile) {
  const file = uploadFile.raw as File;
  let fileType = getFileType(file);
  let options = getOptions('open');
  let spread = toRaw(spreadRef.value)
  if (spread == undefined) return;
  spreadFileName.value = file.name
  if (fileType === 'sjs') {
    spread.open(file,
        function () {
        },
        function () {
        },
        options);
  } else {
    spread.import(file, function () {
      spread.removeSheet(0) // 删除默认的 授权说明sheet
    }, function () {
    }, options);
  }
}

// 清空工作簿内容
function clearWorkBook(reset: boolean) {
  let spread = toRaw(spreadRef.value)
  if (spread == undefined) return
  const count = spread.getSheetCount()
  for (let i = 0; i < count; i++) {
    spread.getSheet(i).reset()
  }
  if (reset) {
    spread.destroy();
    spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), {sheetCount: 3, font: "12pt Arial"});
    initSpread(spread)
    spreadFileName.value = ''
  }
}

//查看缓存中的文件
function handleCacheFile(index: number) {
  const file = fileList.value[index].raw as File
  let fileType = getFileType(file);
  let options = getOptions('open');
  let spread = toRaw(spreadRef.value)
  if (spread == undefined) return;
  spreadFileName.value = file.name
  if (fileType === 'sjs') {
    spread.open(file,
        function () {
        },
        function () {
        },
        options);
  } else {
    spread.import(file, function () {
      spread.removeSheet(0) // 删除默认的 授权说明sheet
    }, function () {
    }, options);
  }
}

// 删除缓存中的文件
function handleDeleteCacheFile(index: number) {
  if (spreadFileName.value === fileList.value[index].name) {
    clearWorkBook(true)
  }
  fileList.value.splice(index, 1)
}

// 磁盘文件列表
let savedFileList = ref<string[]>([]);

// 获取磁盘文件列表
function apiGetFileList() {
  fetch('http://localhost:3000/templates', {
    method: "GET"
  }).then(res => {
    if (res.ok) {
      res.json().then(data => {
        savedFileList.value = data
      })
    } else {
      console.error('Failed to fetch file list');
    }
  })
}

// 刷新表格中公式的计算结果
function reCalculateFormulaValue() {

}

onMounted(() => {
  apiGetFileList()
})
</script>

<style scoped lang="less">
.index-container {
  width: 100%;
  height: calc(100% - 60px);
  display: flex;

  &-workbook {
    width: 80%;
    height: 100%;
    transition: width 0.5s;
  }

  &-multitask {
    height: 100%;
    padding: 5px;
    overflow: hidden;
    transition: width 0.5s;
  }

  &-operations {
    width: 20%;
    height: 100%;
    box-sizing: border-box;
    padding: 20px;
  }
}

.sample-spreadsheets {
  width: 100%;
  height: calc(100% - 25px);
  overflow: hidden;
  transition: width 0.5s;
}

.file-list {
  margin-top: 10px;
  width: 100%;
  max-height: 200px;
  overflow: hidden;
  text-align: left;

  &-item {
    height: 30px;
    line-height: 30px;
    cursor: pointer;
    font-size: 13px;
    color: #606266;
    background-color: transparent;
    padding-left: 5px;
    padding-right: 15px;
    display: flex;
    justify-content: space-between;
    align-items: center;

    &:hover {
      border-radius: 8px;
      color: #898c8f;
      background-color: #d9e8f6;
    }
  }
}
</style>
